package org.chipu.jnids.alert;

import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.chipu.shared.util.singletons.LinuxUtils;
import org.chipu.shared.util.singletons.OSUtils;

/** Listens events from snort through 2.8 experimental unsock output. */
public final class UnixSocketSnortListener extends Thread implements AlertListener {
	private final Logger logger;
	private final File namedPipe;
	private final AlertProcessor<SnortEvent> ap;
	private static final String endCommand = "kill";

	public UnixSocketSnortListener(AlertProcessor<SnortEvent> ap, Logger logger, File logDir) {
		if (!OSUtils.isUnix())
			throw new UnsupportedOperationException("UnixSocketSnortListener can only be used under Unix");
		this.ap = ap;
		this.logger = logger == null? Logger.getLogger(getClass().getSimpleName()): logger;
		namedPipe = new File(logDir.getAbsolutePath() + "/snort_alert");
		// setDaemon(true);
		start();
	}

	/**
	 * <pre>
	 * #!/usr/bin/perl
	 * 	# Include the socket libraries
	 * 	use IO::Socket;
	 * 
	 * 	# This is the template to capture the Alert Name
	 * 	# Edit this to get the additional packets.
	 * 	$TEMPLATE = &quot;A256 A*&quot;;
	 * 
	 * 	# Release the socket if it already exists
	 * 	unlink &quot;/var/log/snort/snort_alert&quot;;
	 * 
	 * 	# In case of user termination - exit gracefully.
	 * 	$SIG{TERM} = $SIG{INT} = sub { exit 0 };
	 * 
	 * 	# Open up the socket.
	 * 	my $client = IO::Socket::UNIX-&gt;new(Type =&gt; SOCK_DGRAM, Local =&gt; &quot;/var/log/snort/snort_alert&quot;) or die &quot;Socket: $@&quot;;
	 * 	print STDOUT &quot;Socket Open ... \n&quot;;
	 * 
	 * 	# Loop receiving data from the socket, pulling out the alert name and printing it.
	 *  my $data;
	 * 	while (true) {
	 *  	recv($client, $data, 1024, 0);
	 * 		&#064;FIELDS = unpack($TEMPLATE, $data);
	 * 		print &quot;@FIELDS[0] \n&quot;;
	 * 		}
	 * 
	 * 	# At termination close up the socket again.
	 * 	END {unlink &quot;/var/log/snort/snort_alert&quot;;}
	 * </pre>
	 */

	@Override
	public void run() {
		try {
			LinuxUtils.createNamedPipe(namedPipe.getAbsolutePath(), new LinuxUtils.Permissions("g+w"));
			// IMPROVE use the current owner
			LinuxUtils.setGroup(namedPipe, "rgarcia", "nids");
		} catch(Exception ex) {
			logger.log(Level.SEVERE, "Error creating named pipe", ex);
		}
		listen();
	}

	private void listen() {
		BufferedReader logPipeReader = null;
		try {
			logger.fine("SnortListener started");
			StringBuilder s;
			do {
				logPipeReader = new BufferedReader(new FileReader(namedPipe));
				s = new StringBuilder();
				int character = -1;
				do {
					if (character != -1 && character != 10)
						s.append(new Character((char) character));
					character = logPipeReader.read();
				} while(character != -1);
				logger.finer("Received '" + s + "'");
				// TODOLATER read snort event
				// BufferedOutputStream bos = new BufferedOutputStream(
				// new FileOutputStream("/home/rgarcia/alertSnort.test",
				// true));
				// bos.write("\r\n".getBytes());
				// bos.write(event.getBytes());
				// bos.flush();
				// bos.close();
				logPipeReader.close();
				ap.eventArrived(null);
			} while(!endCommand.equals(s.toString()));
		} catch(FileNotFoundException ex) {
			logger.warning("Named pipe not found: " + namedPipe.getAbsolutePath());
		} catch(IOException ex) {
			logger.log(Level.SEVERE, "", ex);
		} finally {
			try {
				if (logPipeReader != null)
					logPipeReader.close();
			} catch(IOException ex) {
				logger.log(Level.WARNING, "", ex);
			}
		}
		logger.fine("SnortListener stopped");
		if (namedPipe.delete())
			logger.fine("Pipe deleted successfully");
		else
			namedPipe.deleteOnExit();
	}
}